﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace NanoMvvm
{
    public class WeakEventContainer<TSender, TArgs> : IEventContainer<TSender, TArgs> where TArgs : EventArgs where TSender : class
    {
        private readonly List<WeakEventHandlerContainer<TSender, TArgs>> handlers;

        public WeakEventContainer()
        {
            handlers = new List<WeakEventHandlerContainer<TSender, TArgs>>();
        }

        public bool AddHandler(Action<TSender, TArgs> handler)
        {
            if (handler == null) throw new ArgumentNullException("handler");

            if (handlers.Any(handlerContainer => handlerContainer.Equals(handler)))
            {
                return false;
            }

            handlers.Add(new WeakEventHandlerContainer<TSender, TArgs>(handler));
            return true;
        }

        public bool RemoveHandler(Action<TSender, TArgs> handler)
        {
            WeakEventHandlerContainer<TSender, TArgs> tmp = null;
            foreach (WeakEventHandlerContainer<TSender, TArgs> handlerContainer in handlers)
            {
                if (handlerContainer.Equals(handler))
                {
                    tmp = handlerContainer;
                }
            }

            if(tmp == null) return false;

            handlers.Remove(tmp);

            return true;
        }

        public void InvokeAll(TSender sender = null, TArgs args = default(TArgs))
        {
            var toCall = new List<WeakEventHandlerContainer<TSender, TArgs>>();

            for (int i = handlers.Count - 1; i >= 0; i--)
            {
                var handler = handlers[i];

                if (!handler.IsTargetAlive)
                {
                    // Clean up old handlers that have been collected
                    handlers.RemoveAt(i);
                }
                else
                {
                    toCall.Add(handler);
                }
            }

            // Call the handlers that we snapshotted
            foreach (var handlerContainer in toCall)
            {
                handlerContainer.Invoke(sender, args);
            }
        }

        public IEnumerable<Action<TSender, TArgs>> GetHandlers()
        {
            return handlers.Select(handlerContainer => handlerContainer.ToDelegate());
        }

        public void Clear()
        {
            handlers.Clear();
        }
    }
}